use std::borrow::Cow;

use axum::{
    http::{HeaderValue, StatusCode},
    response::{IntoResponse, Response},
};
use serde::Serialize;

use crate::error::AppErr;

use super::{Cbor, Void};

#[derive(Debug, Serialize)]
struct ErrInfo {
    ec: i32,
    msg: Cow<'static, str>,
}

const HEADER_RESP: &'static str = "resp";
const RESP_OK: &'static str = "ok";
const RESP_FAIL: &'static str = "fail";

impl IntoResponse for AppErr {
    fn into_response(self) -> Response {
        let err_info = match self {
            AppErr::Service(ec, msg) => ErrInfo { ec, msg },
            _ => ErrInfo {
                ec: -1,
                msg: Cow::Owned(self.to_string()),
            },
        };
        if let Ok(body) = serde_cbor::to_vec(&err_info) {
            let mut resp = body.into_response();
            resp.headers_mut()
                .insert(HEADER_RESP, HeaderValue::from_static(RESP_FAIL));
            resp
        } else {
            StatusCode::INTERNAL_SERVER_ERROR.into_response()
        }
    }
}

impl<T: Serialize> IntoResponse for Cbor<T> {
    fn into_response(self) -> Response {
        if let Ok(body) = serde_cbor::to_vec(&self.0) {
            let mut resp = body.into_response();
            resp.headers_mut()
                .insert(HEADER_RESP, HeaderValue::from_static(RESP_OK));
            resp
        } else {
            StatusCode::INTERNAL_SERVER_ERROR.into_response()
        }
    }
}

impl IntoResponse for Void {
    fn into_response(self) -> Response {
        let mut resp = StatusCode::OK.into_response();
        resp.headers_mut()
            .insert(HEADER_RESP, HeaderValue::from_static(RESP_OK));
        resp
    }
}
